package org.semanticflash.rdf.utils
{
	import com.bigflexlib.core.dependency.DependencySpec;
	import com.bigflexlib.x.flash.events.ValueChangedEvent;
	import com.bigflexlib.x.flash.utils.Timer.TimeConsolidatedValue;
	import com.bigflexlib.x.mx.collections.IList.op.ListFirstOp;
	
	import flash.events.Event;
	
	import mx.collections.ArrayCollection;
	import mx.collections.IList;
	
	import org.semanticflash.rdf.values.RDFValue;
	

	public class GSPOQueryBase extends GSPOTuple
	{

		protected var _list:ArrayCollection;
		[Bindable("listChanged")] // only here to remove binding warnings
		public function get list( ):IList { return _list; }
		
		private var _first:RDFValue;
		[Bindable("firstChanged")]
		public function get first( ):RDFValue { return _first; }

		[Bindable("firstChanged")]
		/**
		 * Consider using this if what you want is first.as3Value. 
		 * This way you will avoid binding warnings
		 * 
		 * @return 
		 * 
		 */
		public function get firstAS3Value( ):* { return _first ? _first.as3Value : null; }

		/**
		 * The term to select. Can be any of (s, p, o)
		 * 
		 * @return 
		 * 
		 */		
		[Bindable("termChanged")]
		public function get term( ):String { return dh.g( termSpec ); }		
		public function set term( v:String ):void { dh.s( termSpec, v ); }		
		private var termSpec:DependencySpec = new DependencySpec("term", "*",null,false,"o");
		
		[Bindable("distinctChanged")]
		public function get distinct( ):Boolean { return dh.g( distinctSpec ); }		
		public function set distinct( v:Boolean ):void { dh.s( distinctSpec, v );}		
		private var distinctSpec:DependencySpec = new DependencySpec("distinct","*",null,false,true);

		[Bindable("offsetChanged")]
		public function get offset( ):int { return dh.g( offsetSpec ); }		
		public function set offset( v:int ):void { dh.s( offsetSpec, v );}		
		private var offsetSpec:DependencySpec = new DependencySpec("offset","*",null,false,0);

		[Bindable("limitChanged")]
		public function get limit( ):int { return dh.g( limitSpec ); }		
		public function set limit( v:int ):void { dh.s( limitSpec, v );}		
		private var limitSpec:DependencySpec = new DependencySpec("limit","*",null,false,1);

		[Bindable("langChanged")]
		public function get lang():Array { return dh.g(langSpec); }		
		public function set lang(v:Array):void { dh.s(langSpec, v); }		
		private var langSpec:DependencySpec = new DependencySpec("lang","*",null,false,null);


		[Bindable("requiredValuesChanged")]
		public function get requiredValues( ):String { return dh.g( requiredValuesSpec ); }		
		public function set requiredValues( v:String ):void { dh.s( requiredValuesSpec, v );}		
		private var requiredValuesSpec:DependencySpec = new DependencySpec("requiredValues", "*",null,false,"sp");
		
		private var listFirstOp:ListFirstOp;
		
		private var tcv:TimeConsolidatedValue;
		
		public function GSPOQueryBase(consolidationDelay:int = 300)
		{
			_list = new ArrayCollection( );
			listFirstOp = new ListFirstOp( _list, listFirstOpChanged );
			tcv = new TimeConsolidatedValue(consolidationDelay);
			tcv.addEventListener(ValueChangedEvent.VALUE_CHANGED, tcv_valueChanged);
			tcv.setValue(0);			
		}

		final override protected function dependenciesUpdated():void
		{
			tcv.setValue( tcv.value + 1 ); // just to restart the internal counter
		}
		
		private function tcv_valueChanged(event:ValueChangedEvent):void
		{
			queryChanged();	
		}
		
		protected function hasRequiredValues():Boolean
		{
			if ( requiredValues == null )
				return true;
			// TODO: swap condition evaluation order below for performance
			if ( requires("s") && ( s == null ) )
				return false;
			if ( requires("p") && ( p == null ) )
				return false;
			if ( requires("o") && ( o == null ) )
				return false;
			if ( requires("g") && ( g == null ) )
				return false;
			return true;
		}
		
		private function requires( term:String ):Boolean
		{
			return ( requiredValues.indexOf( term ) != -1 );
		}
		
		protected function queryChanged( ):void
		{
			// abstract
		}
			
		private function listFirstOpChanged( value:* ):void
		{
			_first = value as RDFValue;
			dispatchEvent( new Event( "firstChanged" ) );					
		}


		
	}
}